home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / network / file-tra / rdist-6.1 / rdist-6 / rdist-6.1.0-linuxpl2 / src / client.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-10-16  |  27.4 KB  |  1,242 lines

  1. /*
  2.  * Copyright (c) 1983 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. static char RCSid[] = 
  36. "$Id: client.c,v 6.73 1994/04/12 00:05:41 mcooper Exp mcooper $";
  37.  
  38. static char sccsid[] = "@(#)client.c";
  39.  
  40. static char copyright[] =
  41. "@(#) Copyright (c) 1983 Regents of the University of California.\n\
  42.  All rights reserved.\n";
  43. #endif /* not lint */
  44.  
  45. /*
  46.  * Routines used in client mode to communicate with remove server.
  47.  */
  48.  
  49. #include "defs.h"
  50.  
  51. /*
  52.  * Update status
  53.  */
  54. #define US_NOTHING     0    /* No update needed */
  55. #define US_NOENT    1    /* Entry does not exist */
  56. #define US_OUTDATE    2    /* Entry is out of date */
  57. #define US_DOCOMP    3    /* Do a binary comparison */
  58. #define US_MODE        4    /* Modes of file differ */
  59.  
  60. struct    linkbuf *ihead = NULL;    /* list of files with more than one link */
  61. char    buf[BUFSIZ];        /* general purpose buffer */
  62. u_char    respbuff[BUFSIZ];    /* Response buffer */
  63. char    target[BUFSIZ];        /* target/source directory name */
  64. char    source[BUFSIZ];        /* source directory name */
  65. char    *ptarget;        /* pointer to end of target name */
  66. char    *Tdest;            /* pointer to last T dest*/
  67. struct namelist    *updfilelist = NULL; /* List of updated files */
  68.  
  69. static int sendit();
  70.  
  71. /*
  72.  * return remote file pathname (relative from target)
  73.  */
  74. char *remfilename(src, dest, path, rname, destdir)
  75.     char *src, *dest, *path, *rname;
  76.     int destdir;
  77. {
  78.     extern struct namelist *filelist;
  79.     register char *lname, *cp;
  80.     static char buff[BUFSIZ];
  81.     int srclen, pathlen;
  82.     char *p;
  83.  
  84.  
  85.     debugmsg(DM_MISC, 
  86.          "remfilename: src=%s dest=%s path=%s rname=%s destdir=%d\n",
  87.         A(src), A(dest), A(path), A(rname), destdir);
  88.  
  89.     if (!dest) {
  90.         debugmsg(DM_MISC, "remfilename: remote filename=%s\n", path);
  91.         return(path);
  92.     }
  93.  
  94.     if (!destdir) {
  95.         debugmsg(DM_MISC, "remfilename: remote filename=%s\n", dest);
  96.         return(dest);
  97.     }
  98.  
  99.     buff[0] = CNULL;
  100.     lname = buff;
  101.     if (path && *path) {
  102.         cp = strrchr(path, '/');
  103.          if (cp == NULL)
  104.             (void) sprintf(buff, "%s/%s", dest, path);
  105.         else {
  106.             srclen = strlen(src);
  107.             pathlen = strlen(path);
  108.             if (srclen >= pathlen)
  109.                 cp++; /* basename(path) */
  110.             else {
  111.                 if (filelist && filelist->n_next == NULL)
  112.                     /* path relative to src */
  113.                     cp = path + srclen;
  114.                 else {
  115.                     if ((p = strrchr(src, '/')))
  116.                         cp = path + srclen - strlen(p);
  117.                     else
  118.                         cp = path;
  119.                 }
  120.             }
  121.             if ((*cp != '/') && *cp)
  122.                 (void) sprintf(buff, "%s/%s", dest, cp);
  123.             else
  124.                 (void) sprintf(buff, "%s%s", dest, cp);
  125.         }
  126.     } else
  127.         strcpy(lname, dest);
  128.  
  129.     debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname);
  130.  
  131.     return(lname);
  132. }
  133.  
  134. /*
  135.  * Return true if name is in the list.
  136.  */
  137. int inlist(list, file)
  138.     struct namelist *list;
  139.     char *file;
  140. {
  141.     register struct namelist *nl;
  142.  
  143.     for (nl = list; nl != NULL; nl = nl->n_next)
  144.         if (strcmp(file, nl->n_name) == 0)
  145.             return(1);
  146.     return(0);
  147. }
  148.  
  149. /*
  150.  * Run any special commands for this file
  151.  */
  152. static void runspecial(starget, opts, rname, destdir)
  153.     char *starget;
  154.     opt_t opts;
  155.     char *rname;
  156.     int destdir;
  157. {
  158.     register struct subcmd *sc;
  159.     extern struct subcmd *subcmds;
  160.     char *rfile;
  161.  
  162.      rfile = remfilename(source, Tdest, target, rname, destdir);
  163.  
  164.     for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
  165.         if (sc->sc_type != SPECIAL)
  166.             continue;
  167.         if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
  168.             continue;
  169.         message(MT_CHANGE, "special \"%s\"", sc->sc_name);
  170.         if (IS_ON(opts, DO_VERIFY))
  171.             continue;
  172.         (void) sendcmd(C_SPECIAL,
  173.             "%s=%s;%s=%s;%s=%s;export %s %s %s;%s",
  174.             E_LOCFILE, starget,
  175.             E_REMFILE, rfile,
  176.             E_BASEFILE, basename(rfile),
  177.             E_LOCFILE, E_REMFILE, E_BASEFILE,
  178.             sc->sc_name);
  179.         while (response() > 0)
  180.             ;
  181.     }
  182. }
  183.  
  184. /*
  185.  * If we're doing a target with a "cmdspecial" in it, then
  186.  * save the name of the file being updated for use with "cmdspecial".
  187.  */
  188. static void addcmdspecialfile(starget, rname, destdir)
  189.     char *starget;
  190.     char *rname;
  191.     int destdir;
  192. {
  193.     char *rfile;
  194.     struct namelist *new;
  195.     register struct subcmd *sc;
  196.     extern struct subcmd *subcmds;
  197.     int isokay = 0;
  198.  
  199.      rfile = remfilename(source, Tdest, target, rname, destdir);
  200.  
  201.     for (sc = subcmds; sc != NULL && !isokay; sc = sc->sc_next) {
  202.         if (sc->sc_type != CMDSPECIAL)
  203.             continue;
  204.         if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
  205.             continue;
  206.         isokay = TRUE;
  207.     }
  208.  
  209.     if (isokay) {
  210.         new = (struct namelist *) xmalloc(sizeof(struct namelist));
  211.         new->n_name = strdup(rfile);
  212.         new->n_next = updfilelist;
  213.         updfilelist = new;
  214.     }
  215. }
  216.  
  217. /*
  218.  * Free the file list
  219.  */
  220. static void freecmdspecialfiles()
  221. {
  222.     register struct namelist *ptr, *save;
  223.  
  224.     for (ptr = updfilelist; ptr; ) {
  225.         if (ptr->n_name) (void) free(ptr->n_name);
  226.         save = ptr->n_next;
  227.         (void) free(ptr);
  228.         if (save)
  229.             ptr = save->n_next;
  230.         else
  231.             ptr = NULL;
  232.     }
  233.     updfilelist = NULL;
  234. }
  235.  
  236. /*
  237.  * Run commands for an entire cmd
  238.  */
  239. extern void runcmdspecial(cmd, filev, opts)
  240.     struct cmd *cmd;
  241.     char **filev;
  242.     opt_t opts;
  243. {
  244.     register struct subcmd *sc;
  245.     register struct namelist *f;
  246.     register char **cpp;
  247.     char *file;
  248.     int first = TRUE;
  249.  
  250.     for (sc = cmd->c_cmds; sc != NULL; sc = sc->sc_next) {
  251.         if (sc->sc_type != CMDSPECIAL)
  252.             continue;
  253.         message(MT_CHANGE, "cmdspecial \"%s\"", sc->sc_name);
  254.         if (IS_ON(opts, DO_VERIFY))
  255.             continue;
  256.         /* Send all the file names */
  257.         for (f = updfilelist; f != NULL; f = f->n_next) {
  258.             if (first) {
  259.                 (void) sendcmd(C_CMDSPECIAL, NULL);
  260.                 if (response() < 0)
  261.                     return;
  262.                 first = FALSE;
  263.             }
  264.             (void) sendcmd(RC_FILE, f->n_name);
  265.             if (response() < 0)
  266.                 return;
  267.         }
  268.         if (first) {
  269.             (void) sendcmd(C_CMDSPECIAL, NULL);
  270.             if (response() < 0)
  271.                 return;
  272.             first = FALSE;
  273.         }
  274.         /* Send command to run and wait for it to complete */
  275.         (void) sendcmd(RC_COMMAND, sc->sc_name);
  276.         while (response() > 0)
  277.             ;
  278.         first = TRUE;    /* Reset in case there are more CMDSPECIAL's */
  279.     }
  280.     freecmdspecialfiles();
  281. }
  282.  
  283. /*
  284.  * For security, reject filenames that contains a newline
  285.  */
  286. int checkfilename(name)
  287.     char *name;
  288. {
  289.     register char *cp;
  290.  
  291.     if (strchr(name, '\n')) {
  292.         for (cp = name; *cp; cp++)
  293.             if (*cp == '\n')
  294.                 *cp = '?';
  295.         message(MT_NERROR, 
  296.             "Refuse to handle filename containing newline: %s",
  297.             name);
  298.         return(-1);
  299.     }
  300.  
  301.     return(0);
  302. }
  303.  
  304. /*
  305.  * Save and retrieve hard link info
  306.  */
  307. static struct linkbuf *linkinfo(statp)
  308.     struct stat *statp;
  309. {
  310.     struct linkbuf *lp;
  311.  
  312.     for (lp = ihead; lp != NULL; lp = lp->nextp)
  313.         if (lp->inum == statp->st_ino && lp->devnum == statp->st_dev) {
  314.             lp->count--;
  315.             return(lp);
  316.         }
  317.  
  318.     lp = (struct linkbuf *) xmalloc(sizeof(*lp));
  319.     lp->nextp = ihead;
  320.     ihead = lp;
  321.     lp->inum = statp->st_ino;
  322.     lp->devnum = statp->st_dev;
  323.     lp->count = statp->st_nlink - 1;
  324.     (void) strcpy(lp->pathname, target);
  325.     (void) strcpy(lp->src, source);
  326.     if (Tdest)
  327.         (void) strcpy(lp->target, Tdest);
  328.     else
  329.         *lp->target = CNULL;
  330.  
  331.     return((struct linkbuf *) NULL);
  332. }
  333.  
  334. /*
  335.  * Send a hardlink
  336.  */
  337. static int sendhardlink(opts, lp, rname, destdir)
  338.     opt_t opts;
  339.     struct linkbuf *lp;
  340.     char *rname;
  341.     int destdir;
  342. {
  343.     static char buff[MAXPATHLEN];
  344.     char *lname;    /* name of file to link to */
  345.  
  346.     debugmsg(DM_MISC, 
  347.            "sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n",
  348.          rname, lp->pathname, lp->src, lp->target);
  349.          
  350.     if (*lp->target == CNULL)
  351.         (void) sendcmd(C_RECVHARDLINK, "%o %s %s", 
  352.                    opts, lp->pathname, rname);
  353.     else {
  354.         lname = buff;
  355.         strcpy(lname, remfilename(lp->src, lp->target, 
  356.                       lp->pathname, rname, 
  357.                       destdir));
  358.         debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname);
  359.         (void) sendcmd(C_RECVHARDLINK, "%o %s %s", 
  360.                    opts, lname, rname);
  361.     }
  362.  
  363.     return(response());
  364. }
  365.  
  366. /*
  367.  * Send a file
  368.  */
  369. static int sendfile(rname, opts, stb, user, group, destdir)
  370.     char *rname;
  371.     opt_t opts;
  372.     struct stat *stb;
  373.     char *user, *group;
  374.     int destdir;
  375. {
  376.     int goterr, f;
  377.     off_t i;
  378.  
  379.     if (stb->st_nlink > 1) {
  380.         struct linkbuf *lp;
  381.         
  382.         if ((lp = linkinfo(stb)) != NULL)
  383.             return(sendhardlink(opts, lp, rname, destdir));
  384.     }
  385.  
  386.     if ((f = open(target, O_RDONLY)) < 0) {
  387.         error("%s: open for read failed: %s", target, SYSERR);
  388.         return(-1);
  389.     }
  390.  
  391.     /*
  392.      * Send file info
  393.      */
  394.     (void) sendcmd(C_RECVREG, "%o %04o %ld %ld %ld %s %s %s", 
  395.                opts, stb->st_mode & 07777, stb->st_size, 
  396.                stb->st_mtime, stb->st_atime,
  397.                user, group, rname);
  398.     if (response() < 0) {
  399.         (void) close(f);
  400.         return(-1);
  401.     }
  402.  
  403.     debugmsg(DM_MISC, "Send file '%s' %d bytes\n", rname, stb->st_size);
  404.  
  405.     /*
  406.      * Set remote time out alarm handler.
  407.      */
  408.     (void) signal(SIGALRM, sighandler);
  409.  
  410.     /*
  411.      * Actually transfer the file
  412.      */
  413.     goterr = 0;
  414.     for (i = 0; i < stb->st_size; i += BUFSIZ) {
  415.         int amt = BUFSIZ;
  416.  
  417.         (void) alarm(rtimeout);
  418.         if (i + amt > stb->st_size)
  419.             amt = stb->st_size - i;
  420.         if (read(f, buf, amt) != amt) {
  421.             error("%s: File changed size", target);
  422.             err();
  423.             ++goterr;
  424.             /*
  425.              * XXX - We have to keep going because the
  426.              * server expects to receive a fixed number
  427.              * of bytes that we specified as the file size.
  428.              * We need Out Of Band communication to handle
  429.              * this situation gracefully.
  430.              */
  431.         }
  432.         {
  433.             int ramt, wamt=amt, woff=0;
  434.             while((ramt=write(rem_w,buf+woff,wamt)) > 0) {
  435.                 woff += ramt; wamt -= ramt;
  436.             }
  437.             if (ramt < 0) {
  438.                 error("%s: Error writing to client: %s", 
  439.                       target, SYSERR);
  440.                 err();
  441.                 ++goterr;
  442.                 break;
  443.             }
  444.         }
  445.         (void) alarm(0);
  446.     }
  447.  
  448.     (void) alarm(0);    /* Insure alarm is off */
  449.     (void) close(f);
  450.  
  451.     debugmsg(DM_MISC, "Send file '%s' %s.\n", 
  452.          (goterr) ? "failed" : "complete", rname);
  453.  
  454.     /*
  455.      * Check for errors and end send
  456.      */
  457.     if (goterr)
  458.         return(-1);
  459.     else {
  460.         ack();
  461.         f = response();
  462.         if (f < 0)
  463.             return(-1);
  464.         else if (f == 0 && IS_ON(opts, DO_COMPARE))
  465.             return(0);
  466.  
  467.         runspecial(target, opts, rname, destdir);
  468.         addcmdspecialfile(target, rname, destdir);
  469.  
  470.         return(0);
  471.     }
  472. }
  473.  
  474. /*
  475.  * Check for files on the machine being updated that are not on the master
  476.  * machine and remove them.
  477.  *
  478.  * Return < 0 on error.
  479.  * Return 0 if nothing happened.
  480.  * Return > 0 if anything is updated.
  481.  */
  482. static int rmchk(opts)
  483.     opt_t opts;
  484. {
  485.     register u_char *s;
  486.     struct stat stb;
  487.     int didupdate = 0;
  488.     int n;
  489.  
  490.     debugmsg(DM_CALL, "rmchk()\n");
  491.  
  492.     /*
  493.      * Tell the remote to clean the files from the last directory sent.
  494.      */
  495.     (void) sendcmd(C_CLEAN, "%o", IS_ON(opts, DO_VERIFY));
  496.     if (response() < 0)
  497.         return(-1);
  498.  
  499.     for ( ; ; ) {
  500.         n = remline(s = respbuff, sizeof(respbuff), TRUE);
  501.         if (n <= 0) {
  502.             error("rmchk: unexpected control record");
  503.             return(didupdate);
  504.         }
  505.  
  506.         switch (*s++) {
  507.         case CC_QUERY: /* Query if file should be removed */
  508.             /*
  509.              * Return the following codes to remove query.
  510.              * CC_NO -- file exists - DON'T remove.
  511.              * CC_YES -- file doesn't exist - REMOVE.
  512.              */
  513.             (void) sprintf(ptarget, "%s%s", 
  514.                        (ptarget[-1] == '/' ? "" : "/"), s);
  515.             debugmsg(DM_MISC, "check %s\n", target);
  516.             if (except(target))
  517.                 (void) sendcmd(CC_NO, NULL);
  518.             else if (lstat(target, &stb) < 0) {
  519.                 if (sendcmd(CC_YES, NULL) == 0)
  520.                     didupdate = 1;
  521.             } else
  522.                 (void) sendcmd(CC_NO, NULL);
  523.             break;
  524.  
  525.         case CC_END:
  526.             *ptarget = CNULL;
  527.             ack();
  528.             return(didupdate);
  529.  
  530.         case C_LOGMSG:
  531.             if (n > 0)
  532.                 message(MT_INFO, "%s", s);
  533.             break;
  534.  
  535.         case C_ERRMSG:
  536.             message(MT_NERROR, "%s", s);
  537.             return(didupdate);
  538.  
  539.         case C_FERRMSG:
  540.             message(MT_FERROR, "%s", s);
  541.             finish();
  542.  
  543.         default:
  544.             error("rmchk: unexpected response '%s'", respbuff);
  545.             err();
  546.         }
  547.     }
  548.     /*NOTREACHED*/
  549. }
  550.  
  551. /*
  552.  * Send a directory
  553.  *
  554.  * Return < 0 on error.
  555.  * Return 0 if nothing happened.
  556.  * Return > 0 if anything is updated.
  557.  */
  558. static int senddir(rname, opts, stb, user, group, destdir)
  559.     char *rname;
  560.     opt_t opts;
  561.     struct stat *stb;
  562.     char *user, *group;
  563.     int destdir;
  564. {
  565.     DIRENTRY *dp;
  566.     DIR *d;
  567.     char *optarget, *cp;
  568.     int len;
  569.     int didupdate = 0;
  570.  
  571.     /*
  572.      * Send recvdir command in recvit() format.
  573.      */
  574.     (void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s", 
  575.                opts, stb->st_mode & 07777, user, group, rname);
  576.     if (response() < 0)
  577.         return(-1);
  578.  
  579.     /*
  580.      * Don't descend into directory
  581.      */
  582.     if (IS_ON(opts, DO_NODESCEND))
  583.         return(0);
  584.  
  585.     if (IS_ON(opts, DO_REMOVE))
  586.         if (rmchk(opts) > 0)
  587.             ++didupdate;
  588.     
  589.     if ((d = opendir(target)) == NULL) {
  590.         error("%s: opendir failed: %s", target, SYSERR);
  591.         return(-1);
  592.     }
  593.  
  594.     optarget = ptarget;
  595.     len = ptarget - target;
  596.     while (dp = readdir(d)) {
  597.         if (!strcmp(dp->d_name, ".") ||
  598.             !strcmp(dp->d_name, ".."))
  599.             continue;
  600.         if (len + 1 + (int) strlen(dp->d_name) >= MAXPATHLEN - 1) {
  601.             error("%s/%s: Name too long", target,
  602.                   dp->d_name);
  603.             continue;
  604.         }
  605.         ptarget = optarget;
  606.         if (ptarget[-1] != '/')
  607.             *ptarget++ = '/';
  608.         cp = dp->d_name;
  609.         while (*ptarget++ = *cp++)
  610.             ;
  611.         ptarget--;
  612.         if (sendit(dp->d_name, opts, destdir) > 0)
  613.             didupdate = 1;
  614.     }
  615.     (void) closedir(d);
  616.  
  617.     (void) sendcmd(C_END, NULL);
  618.     (void) response();
  619.  
  620.     ptarget = optarget;
  621.     *ptarget = CNULL;
  622.  
  623.     return(didupdate);
  624. }
  625.  
  626. /*
  627.  * Send a link
  628.  */
  629. static int sendlink(rname, opts, stb, user, group, destdir)
  630.     char *rname;
  631.     opt_t opts;
  632.     struct stat *stb;
  633.     char *user;
  634.     char *group;
  635.     int destdir;
  636. {
  637.     int sizerr, f, n;
  638.     static char tbuf[BUFSIZ];
  639.     char lbuf[MAXPATHLEN];
  640.     u_char *s;
  641.  
  642.     debugmsg(DM_CALL, "sendlink(%s, %x, stb, %d)\n", rname, opts, destdir);
  643.  
  644.     if (stb->st_nlink > 1) {
  645.         struct linkbuf *lp;
  646.         
  647.         if ((lp = linkinfo(stb)) != NULL)
  648.             return(sendhardlink(opts, lp, rname, destdir));
  649.     }
  650.  
  651.     /*
  652.      * Gather and send basic link info
  653.      */
  654.     (void) sendcmd(C_RECVSYMLINK, "%o %04o %ld %ld %ld %s %s %s", 
  655.                opts, stb->st_mode & 07777, stb->st_size, 
  656.                stb->st_mtime, stb->st_atime,
  657.                user, group, rname);
  658.     if (response() < 0)
  659.         return(-1);
  660.  
  661.     /*
  662.      * Gather and send additional link info
  663.      */
  664.     sizerr = (readlink(target, lbuf, sizeof(lbuf)) != stb->st_size);
  665.     (void) sprintf(tbuf, "%.*s", stb->st_size, lbuf);
  666.     (void) sendcmd(C_NONE, "%s\n", tbuf);
  667.  
  668.     if (sizerr) {
  669.         error("%s: file changed size", target);
  670.         err();
  671.     } else
  672.         ack();
  673.  
  674.     /*
  675.      * Check response
  676.      */
  677.     f = response();
  678.     if (f < 0)
  679.         return(-1);
  680.     else if (f == 0 && IS_ON(opts, DO_COMPARE))
  681.         return(0);
  682.  
  683.     /*
  684.      * Read and process responses from server.
  685.      * The server may send multiple messages regarding
  686.      * file deletes if the remote target is a directory.
  687.      */
  688.     for (;;) {
  689.         n = remline(s = respbuff, sizeof(respbuff), TRUE);
  690.         if (n == -1)    /* normal EOF */
  691.             return(0);
  692.         if (n == 0) {
  693.             error("expected control record");
  694.             continue;
  695.         }
  696.         
  697.         switch (*s++) {
  698.         case C_END:    /* End of send operation */
  699.             *ptarget = CNULL;
  700.             ack();
  701.             runspecial(target, opts, rname, destdir);
  702.             addcmdspecialfile(target, rname, destdir);
  703.             return(0);
  704.             
  705.         case C_LOGMSG:
  706.             if (n > 0)
  707.                 message(MT_INFO, "%s", s);
  708.             break;
  709.  
  710.         case C_ERRMSG:
  711.             message(MT_NERROR, "%s", s);
  712.             return(-1);
  713.  
  714.         case C_FERRMSG:
  715.             message(MT_FERROR, "%s", s);
  716.             finish();
  717.  
  718.         default:
  719.             error("install link: unexpected response '%s'", 
  720.                   respbuff);
  721.             err();
  722.         }
  723.     }
  724.     /*NOTREACHED*/
  725. }
  726.  
  727. /*
  728.  * Check to see if file needs to be updated on the remote machine.
  729.  * Returns:
  730.  *     US_NOTHING    - no update
  731.  *    US_NOENT    - remote doesn't exist
  732.  *    US_OUTDATE    - out of date
  733.  *    US_DOCOMP    - comparing binaries to determine if out of date
  734.  *    US_MODE        - File modes do not match
  735.  */
  736. static int update(rname, opts, statp)
  737.     char *rname;
  738.     opt_t opts;
  739.     struct stat *statp;
  740. {
  741.     register off_t size;
  742.     register time_t mtime;
  743.     unsigned short lmode;
  744.     unsigned short rmode;
  745.     char *owner = NULL, *group = NULL;
  746.     int done, n;
  747.     u_char *cp;
  748.  
  749.     debugmsg(DM_CALL, "update(%s, 0x%x, 0x%x)\n", rname, opts, statp);
  750.  
  751.     if (IS_ON(opts, DO_NOEXEC))
  752.         if (isexec(target, statp)) {
  753.             debugmsg(DM_MISC, "%s is an executable\n", target);
  754.             return(US_NOTHING);
  755.         }
  756.  
  757.     /*
  758.      * Check to see if the file exists on the remote machine.
  759.      */
  760.     (void) sendcmd(C_QUERY, "%s", rname);
  761.  
  762.     for (done = 0; !done;) {
  763.         n = remline(cp = respbuff, sizeof(respbuff), TRUE);
  764.         if (n <= 0) {
  765.             error("update: unexpected control record in response to query");
  766.             return(US_NOTHING);
  767.         }
  768.  
  769.         switch (*cp++) {
  770.         case QC_ONNFS:  /* Resides on a NFS */
  771.             debugmsg(DM_MISC,
  772.                  "update: %s is on a NFS.  Skipping...\n", 
  773.                  rname);
  774.             return(US_NOTHING);
  775.  
  776.         case QC_SYM:  /* Is a symbolic link */
  777.             debugmsg(DM_MISC,
  778.                  "update: %s is a symlink.  Skipping...\n", 
  779.                  rname);
  780.             return(US_NOTHING);
  781.  
  782.         case QC_ONRO:  /* Resides on a Read-Only fs */
  783.             debugmsg(DM_MISC,
  784.                  "update: %s is on a RO fs.  Skipping...\n", 
  785.                  rname);
  786.             return(US_NOTHING);
  787.             
  788.         case QC_YES:
  789.             done = 1;
  790.             break;
  791.  
  792.         case QC_NO:  /* file doesn't exist so install it */
  793.             return(US_NOENT);
  794.  
  795.         case C_ERRMSG:
  796.             if (cp)
  797.                 message(MT_NERROR, "%s", cp);
  798.             return(US_NOTHING);
  799.  
  800.         case C_FERRMSG:
  801.             if (cp)
  802.                 message(MT_FERROR, "%s", cp);
  803.             finish();
  804.  
  805.         case C_NOTEMSG:
  806.             if (cp)
  807.                 message(MT_NOTICE, "%s", cp);
  808.             break;
  809.             /* Goto top of loop */
  810.  
  811.         default:
  812.             error("update: unexpected response to query '%s'", cp);
  813.             return(US_NOTHING);
  814.         }
  815.     }
  816.  
  817.     /*
  818.      * Target exists, but no other info passed
  819.      */
  820.     if (n <= 1 || !S_ISREG(statp->st_mode))
  821.         return(US_OUTDATE);
  822.  
  823.     if (IS_ON(opts, DO_COMPARE))
  824.         return(US_DOCOMP);
  825.  
  826.     /*
  827.      * Parse size
  828.      */
  829.     size = strtol(cp, &cp, 10);
  830.     if (*cp++ != ' ') {
  831.         error("update: size not delimited");
  832.         return(US_NOTHING);
  833.     }
  834.  
  835.     /*
  836.      * Parse mtime
  837.      */
  838.     mtime = strtol(cp, &cp, 10);
  839.     if (*cp++ != ' ') {
  840.         error("update: mtime not delimited");
  841.         return(US_NOTHING);
  842.     }
  843.  
  844.     /*
  845.      * Parse remote file mode
  846.      */
  847.     rmode = strtol(cp, &cp, 8);
  848.     if (cp && *cp)
  849.         ++cp;
  850.  
  851.     /*
  852.      * Be backwards compatible
  853.      */
  854.     if (cp && *cp != CNULL) {
  855.         /*
  856.          * Parse remote file owner
  857.          */
  858.         owner = strtok((char *)cp, " ");
  859.         if (owner == NULL) {
  860.             error("update: owner not delimited");
  861.             return(US_NOTHING);
  862.         }
  863.  
  864.         /*
  865.          * Parse remote file group
  866.          */
  867.         group = strtok((char *) NULL, " ");
  868.         if (group == NULL) {
  869.             error("update: group not delimited");
  870.             return(US_NOTHING);
  871.         }
  872.     }
  873.  
  874.     /*
  875.      * File needs to be updated?
  876.      */
  877.     lmode = statp->st_mode & 07777;
  878.  
  879.     debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n", 
  880.          rname, lmode, rmode);
  881.     debugmsg(DM_MISC, "update(%s,) size %d mtime %d owner '%s' grp '%s'\n",
  882.          rname, size, mtime, owner, group);
  883.  
  884.     if (statp->st_mtime != mtime) {
  885.         if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) {
  886.             message(MT_WARNING, 
  887.                 "%s: Warning: remote copy is newer",
  888.                 target);
  889.             return(US_NOTHING);
  890.         }
  891.         return(US_OUTDATE);
  892.     }
  893.  
  894.     /*
  895.      * If the mode of a file does not match the local mode, the
  896.      * whole file is updated.  This is done both to insure that
  897.      * a bogus version of the file has not been installed and to
  898.      * avoid having to handle weird cases of chmod'ing symlinks 
  899.      * and such.
  900.      */
  901.     if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) {
  902.         debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n",
  903.              lmode, rmode);
  904.         return(US_OUTDATE);
  905.     }
  906.  
  907.     if (statp->st_size != size) {
  908.         debugmsg(DM_MISC, "size does not match (%d != %d).\n",
  909.              statp->st_size, size);
  910.         return(US_OUTDATE);
  911.     } 
  912.  
  913.     /*
  914.      * Check ownership
  915.      */
  916.     if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
  917.         if (!IS_ON(opts, DO_NUMCHKOWNER)) {
  918.             /* Check by string compare */
  919.             if (strcmp(owner, getusername(statp->st_uid, 
  920.                               target, opts)) != 0) {
  921.                 debugmsg(DM_MISC, 
  922.                      "owner does not match (%s != %s).\n",
  923.                      getusername(statp->st_uid, 
  924.                              target, opts), owner);
  925.                 return(US_OUTDATE);
  926.             }
  927.         } else {
  928.             /* 
  929.              * Check numerically.
  930.              * Allow negative numbers.
  931.              */
  932.             while (*owner && !isdigit(*owner) && (*owner != '-'))
  933.                 ++owner;
  934.             if (owner && atoi(owner) != statp->st_uid) {
  935.                 debugmsg(DM_MISC, 
  936.                      "owner does not match (%d != %s).\n",
  937.                      statp->st_uid, owner);
  938.                 return(US_OUTDATE);
  939.             }
  940.         }
  941.     } 
  942.  
  943.     if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
  944.         if (!IS_ON(opts, DO_NUMCHKGROUP)) {
  945.             /* Check by string compare */
  946.             if (strcmp(group, getgroupname(statp->st_gid, 
  947.                                target, opts)) != 0) {
  948.                 debugmsg(DM_MISC, 
  949.                      "group does not match (%s != %s).\n",
  950.                      getgroupname(statp->st_gid, 
  951.                               target, opts), group);
  952.                 return(US_OUTDATE);
  953.             }
  954.         } else {    
  955.             /* Check numerically */
  956.             /* Allow negative gid */
  957.             while (*group && !isdigit(*group) && (*group != '-'))
  958.                 ++group;
  959.             if (group && atoi(group) != statp->st_gid) {
  960.                 debugmsg(DM_MISC,
  961.                      "group does not match (%d != %s).\n",
  962.                      statp->st_gid, group);
  963.                 return(US_OUTDATE);
  964.             }
  965.         }
  966.     }
  967.  
  968.     return(US_NOTHING);
  969. }
  970.  
  971. /*
  972.  * Stat a file
  973.  */
  974. static int dostat(file, statbuf, opts)
  975.     char *file;
  976.     struct stat *statbuf;
  977.     opt_t opts;
  978. {
  979.     int s;
  980.  
  981.     if (IS_ON(opts, DO_FOLLOW))
  982.         s = stat(file, statbuf);
  983.     else
  984.         s = lstat(file, statbuf);
  985.  
  986.     if (s < 0)
  987.         error("%s: %s failed: %s", file,
  988.               IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR);
  989.     return(s);
  990. }
  991.  
  992. /*
  993.  * Transfer the file or directory in target[].
  994.  * rname is the name of the file on the remote host.
  995.  *
  996.  * Return < 0 on error.
  997.  * Return 0 if nothing happened.
  998.  * Return > 0 if anything is updated.
  999.  */
  1000. static int sendit(rname, opts, destdir)
  1001.     char *rname;
  1002.     opt_t opts;
  1003.     int destdir;
  1004. {
  1005.     static struct stat stb;
  1006.     extern struct subcmd *subcmds;
  1007.     char *user, *group;
  1008.     int u, len;
  1009.     int didupdate = 0;
  1010.  
  1011.     /*
  1012.      * Remove possible accidental newline
  1013.      */
  1014.     len = strlen(rname);
  1015.     if (len > 0 && rname[len-1] == '\n')
  1016.         rname[len-1] = CNULL;
  1017.  
  1018.     if (checkfilename(rname) != 0)
  1019.         return(-1);
  1020.  
  1021.     debugmsg(DM_CALL, "sendit(%s, 0x%x) called\n", rname, opts);
  1022.  
  1023.     if (except(target))
  1024.         return(0);
  1025.  
  1026.     if (dostat(target, &stb, opts) < 0)
  1027.         return(-1);
  1028.  
  1029.     /*
  1030.      * Does rname need updating?
  1031.      */
  1032.     u = update(rname, opts, &stb);
  1033.     debugmsg(DM_MISC, "sendit(%s, 0x%x): update status of %s is %d\n", 
  1034.          rname, opts, target, u);
  1035.  
  1036.     /*
  1037.      * Don't need to update the file, but we may need to save hardlink
  1038.      * info.
  1039.      */
  1040.     if (u == US_NOTHING) {
  1041.         if (S_ISREG(stb.st_mode) && stb.st_nlink > 1)
  1042.             (void) linkinfo(&stb);
  1043.         return(0);
  1044.     }
  1045.  
  1046.     /*
  1047.      * File mode needs changing
  1048.      */
  1049.     if (u == US_MODE) {
  1050.         if (IS_ON(opts, DO_VERIFY)) {
  1051.             message(MT_INFO, "%s: need to chmod to %04o",
  1052.                 target, stb.st_mode & 07777);
  1053.             runspecial(target, opts, rname, destdir);
  1054.             return(1);
  1055.         }
  1056.         message(MT_CHANGE, "%s: chmod to %04o", 
  1057.             target, stb.st_mode & 07777);
  1058.         (void) sendcmd(C_CHMOD, "%o %04o %s",
  1059.                    opts, stb.st_mode & 07777, rname);
  1060.         (void) response();
  1061.         return(1);
  1062.     }
  1063.  
  1064.     user = getusername(stb.st_uid, target, opts);
  1065.     group = getgroupname(stb.st_gid, target, opts);
  1066.  
  1067.     /*
  1068.      * No entry - need to install
  1069.      */
  1070.     if (u == US_NOENT) {
  1071.         if (IS_ON(opts, DO_VERIFY)) {
  1072.             message(MT_INFO, "%s: need to install", target);
  1073.             runspecial(target, opts, rname, destdir);
  1074.             return(1);
  1075.         }
  1076.         if (!IS_ON(opts, DO_QUIET))
  1077.             message(MT_CHANGE, "%s: installing", target);
  1078.         FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE));
  1079.     }
  1080.  
  1081.     /*
  1082.      * Handle special file types, including directories and symlinks
  1083.      */
  1084.     if (S_ISDIR(stb.st_mode)) {
  1085.         if (senddir(rname, opts, &stb, user, group, destdir) > 0)
  1086.             didupdate = 1;
  1087.     } else if (S_ISLNK(stb.st_mode)) {
  1088.         if (u != US_NOENT)
  1089.             FLAG_ON(opts, DO_COMPARE);
  1090.         /*
  1091.          * Since we always send link info to the server
  1092.          * so the server can determine if the remote link
  1093.          * is correct, we never get any acknowledge meant
  1094.          * from the server whether the link was really
  1095.          * updated or not.
  1096.          */
  1097.         (void) sendlink(rname, opts, &stb, user, group, destdir);
  1098.     } else if (S_ISREG(stb.st_mode)) {        
  1099.         if (u == US_OUTDATE) {
  1100.             if (IS_ON(opts, DO_VERIFY)) {
  1101.                 message(MT_INFO, "%s: need to update", target);
  1102.                 runspecial(target, opts, rname, destdir);
  1103.                 return(1);
  1104.             }
  1105.             if (!IS_ON(opts, DO_QUIET))
  1106.                 message(MT_CHANGE, "%s: updating", target);
  1107.         }
  1108.         if (sendfile(rname, opts, &stb, user, group, destdir) == 0)
  1109.             didupdate = 1;
  1110.     } else
  1111.         error("%s: unknown file type", target);
  1112.  
  1113.     return(didupdate);
  1114. }
  1115.     
  1116. /*
  1117.  * Remove temporary files and do any cleanup operations before exiting.
  1118.  */
  1119. extern void cleanup()
  1120. {
  1121.     char *file;
  1122. #ifdef USE_STATDB
  1123.     extern char statfile[];
  1124.  
  1125.     (void) unlink(statfile);
  1126. #endif
  1127.  
  1128.     if (file = getnotifyfile())
  1129.         (void) unlink(file);
  1130. }
  1131.  
  1132. /*
  1133.  * Update the file(s) if they are different.
  1134.  * destdir = 1 if destination should be a directory
  1135.  * (i.e., more than one source is being copied to the same destination).
  1136.  *
  1137.  * Return < 0 on error.
  1138.  * Return 0 if nothing updated.
  1139.  * Return > 0 if something was updated.
  1140.  */
  1141. extern int install(src, dest, ddir, destdir, opts)
  1142.     char *src, *dest;
  1143.      int ddir, destdir;
  1144.     opt_t opts;
  1145. {
  1146.     static char destcopy[MAXPATHLEN];
  1147.     char *rname;
  1148.     int didupdate = 0;
  1149.  
  1150.     debugmsg(DM_CALL,
  1151.         "install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%d) start\n",
  1152.         (src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts);
  1153.     /*
  1154.      * Save source name
  1155.      */
  1156.     if (IS_ON(opts, DO_WHOLE))
  1157.         source[0] = CNULL;
  1158.     else
  1159.         (void) strcpy(source, src);
  1160.  
  1161.     if (dest == NULL) {
  1162.         FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */
  1163.         dest = src;
  1164.     }
  1165.  
  1166.     if (checkfilename(dest) != 0)
  1167.         return(-1);
  1168.  
  1169.     if (nflag || debug) {
  1170.         static char buff[BUFSIZ];
  1171.         char *cp;
  1172.  
  1173.         cp = getondistoptlist(opts);
  1174.         (void) sprintf(buff, "%s%s%s %s %s", 
  1175.                    IS_ON(opts, DO_VERIFY) ? "verify" : "install",
  1176.                    (cp) ? " -o" : "", (cp) ? cp : "", 
  1177.                    src, dest);
  1178.         if (nflag) {
  1179.             printf("%s\n", buff);
  1180.             return(0);
  1181.         } else
  1182.             debugmsg(DM_MISC, "%s\n", buff);
  1183.     }
  1184.  
  1185.     rname = exptilde(target, src);
  1186.     if (rname == NULL)
  1187.         return(-1);
  1188.     ptarget = target;
  1189.     while (*ptarget)
  1190.         ptarget++;
  1191.     /*
  1192.      * If we are renaming a directory and we want to preserve
  1193.      * the directory heirarchy (-w), we must strip off the leading
  1194.      * directory name and preserve the rest.
  1195.      */
  1196.     if (IS_ON(opts, DO_WHOLE)) {
  1197.         while (*rname == '/')
  1198.             rname++;
  1199.         ddir = 1;
  1200.         destdir = 1;
  1201.     } else {
  1202.         rname = strrchr(target, '/');
  1203.         /* Check if no '/' or target ends in '/' */
  1204.         if (rname == NULL || 
  1205.             rname+1 == NULL || 
  1206.             *(rname+1) == CNULL)
  1207.             rname = target;
  1208.         else
  1209.             rname++;
  1210.     }
  1211.  
  1212.     debugmsg(DM_MISC, 
  1213.      "install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n", 
  1214.           target, source, rname, dest, destdir, ddir);
  1215.  
  1216.     /*
  1217.      * Pass the destination file/directory name to remote.
  1218.      */
  1219.      if (ddir)
  1220.         (void) sendcmd(C_DIRTARGET, "%o %s", opts, dest);
  1221.     else
  1222.         (void) sendcmd(C_TARGET, "%o %s", opts, dest);
  1223.     if (response() < 0)
  1224.         return(-1);
  1225.  
  1226.     /*
  1227.      * Save the name of the remote target destination if we are
  1228.      * in WHOLE mode (destdir > 0) or if the source and destination
  1229.      * are not the same.  This info will be used later for maintaining
  1230.      * hardlink info.
  1231.      */
  1232.     if (destdir || (src && dest && strcmp(src, dest))) {
  1233.         (void) strcpy(destcopy, dest);
  1234.         Tdest = destcopy;
  1235.     }
  1236.  
  1237.     didupdate = sendit(rname, opts, destdir);
  1238.     Tdest = 0;
  1239.  
  1240.     return(didupdate);
  1241. }
  1242.